<template>
  <div class="rev-image"></div>
</template>

<script>
/* eslint-disable @typescript-eslint/no-this-alias */
import Konva from 'konva'

export default {
  name: 'RevImage',
  props: {
    url: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      default: ''
    },
    operation: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      stage: null,
      layer: null,
      transformer: null,
      imageNow: null,
      graphNow: null, // 当前创建的图形
      drawing: false,
      pointStart: undefined, // 画裁剪矩形起点
      pointList: [], // 画多边形的点列表
      pointPolygon: null, // 画多边形1（画黑白两条线，实现阴影效果）
      pointPolygon2: null, // 画多边形2（画黑白两条线，实现阴影效果）
      dragPoint: null // 当前拖动的点
    }
  },
  computed: {
    polygonLimit() {
      if (this.operation && this.operation.inputs) {
        const poly = this.operation.inputs.find((ip) => ip.key === 'polygon')
        return poly ? poly.limit : 0
      }
      return 0
    },
    inputTypes() {
      if (this.operation && this.operation.inputs) {
        return this.operation.inputs.map((ipt) => ipt.type)
      }
      return []
    }
  },
  watch: {
    url(val) {
      if (val) {
        this.loadImage()
      }
    },
    type() {
      if (this.graphNow) {
        this.graphNow.destroy()
        this.graphNow = null
      }
      if (this.transformer) {
        this.transformer.destroy()
        this.transformer = null
      }
      if (this.stage) {
        this.stage.off('mousedown')
        this.stage.off('mousemove')
        this.stage.off('mouseup')
      }
    },
    inputTypes(val) {
      console.log(val)
      this.$nextTick(() => {
        if (this.inputTypes.includes('box')) {
          this.addCropLayer()
          this.clearPolygon()
        } else if (this.inputTypes.includes('polygon')) {
          this.drawPolygon()
        } else {
          this.clearPolygon()
        }
      })
    }
  },
  mounted() {
    this.initKonva()
    if (this.url) {
      this.loadImage()
    }
    this.$el.oncontextmenu = function (e) {
      e.returnValue = false
    }
  },
  methods: {
    // 自适应居中图片
    getShowRect(imgObj) {
      const padding = 10
      const naturalWidth = imgObj.naturalWidth
      const naturalHeight = imgObj.naturalHeight
      const width = this.$el.clientWidth
      const height = this.$el.clientHeight
      const aspectCanvas = width / height
      const aspectImage = naturalWidth / naturalHeight
      if (naturalWidth + padding * 2 < width && naturalHeight + padding * 2 < height) {
        return {
          x: (width - naturalWidth) / 2,
          y: (height - naturalHeight) / 2,
          width: naturalWidth,
          height: naturalHeight
        }
      } else if (aspectCanvas < aspectImage) {
        const h = (width - padding * 2) / aspectImage
        return {
          x: padding,
          y: (height - h) / 2,
          width: width - padding * 2,
          height: (width - padding * 2) / aspectImage
        }
      } else {
        const w = (height - padding * 2) * aspectImage
        return {
          x: (width - w) / 2,
          y: padding,
          height: height - padding * 2,
          width: w
        }
      }
    },
    // 初始化canvas
    initKonva() {
      var width = this.$el.clientWidth
      var height = this.$el.clientHeight

      this.stage = new Konva.Stage({
        container: this.$el,
        width: width,
        height: height,
        draggable: true
      })
    },
    // 加载图片
    loadImage() {
      if (this.graphNow) {
        this.graphNow.destroy()
        this.graphNow = null
      }
      if (this.transformer) {
        this.transformer.destroy()
        this.transformer = null
      }
      if (!this.stage) {
        this.initKonva()
      }
      if (this.layer) {
        this.layer.remove()
        this.layer.destroy()
      }
      this.stage.clear()
      var layer = new Konva.Layer()
      this.stage.add(layer)
      if (this.stage) {
        this.stage.off('mousedown')
        this.stage.off('mousemove')
        this.stage.off('mouseup')
        this.stage.off('wheel')
      }

      Konva.Image.fromURL(this.url, (img) => {
        const imgObj = img.attrs.image
        const rect = this.getShowRect(imgObj)

        img.setAttrs({
          width: 300,
          height: 200,
          x: 80,
          y: 100,
          ...rect,
          name: 'image',
          draggable: false
        })

        this.imageNow = img

        layer.add(img)

        // const tr = new Konva.Transformer({
        //   nodes: [img],
        //   resizeEnabled: false,
        //   rotateEnabled: false,
        //   keepRatio: false,
        //   boundBoxFunc: (oldBox, newBox) => {
        //     if (newBox.width < 10 || newBox.height < 10) {
        //       return oldBox;
        //     }
        //     return newBox;
        //   },
        // });
        // layer.add(tr);
        if (this.inputTypes.includes('box')) {
          this.addCropLayer()
          this.clearPolygon()
        } else if (this.inputTypes.includes('polygon')) {
          this.drawPolygon()
        } else {
          this.clearPolygon()
        }
      })
      this.layer = layer

      this.stage.on('wheel', (e) => {
        e.evt.preventDefault()
        var max = 4 // 放大最大的比例
        var min = 0.5 // 缩小最小的比例
        var step = 0.02 // 每次缩放的比例

        var oldScale = this.stage.scaleX()
        var pointerPosition = {
          x: this.stage.width() / 2,
          y: this.stage.height() / 2
        }
        var mousePointTo = {
          x: pointerPosition.x / oldScale - this.stage.x() / oldScale,
          y: pointerPosition.y / oldScale - this.stage.y() / oldScale
        }

        const evt = e.evt

        if (evt.wheelDelta) {
          if (evt.wheelDelta > 0) {
            // 放大
            if (oldScale < max) {
              const newScale = oldScale + step
              this.stage.scale({ x: newScale, y: newScale })

              const newPos = {
                x: -(mousePointTo.x - pointerPosition.x / newScale) * newScale,
                y: -(mousePointTo.y - pointerPosition.y / newScale) * newScale
              }
              this.stage.position(newPos)
            }
          } else {
            // 缩小
            if (oldScale > min) {
              const newScale = oldScale - step
              this.stage.scale({ x: newScale, y: newScale })

              const newPos = {
                x: -(mousePointTo.x - pointerPosition.x / newScale) * newScale,
                y: -(mousePointTo.y - pointerPosition.y / newScale) * newScale
              }
              this.stage.position(newPos)
            }
          }
          const scale = { x: 1 / this.stage.scaleX(), y: 1 / this.stage.scaleX() }
          this.stage.find('Circle').forEach((shape) => shape.scale(scale))
        }
      })
    },
    clearPolygon() {
      this.dragPoint = null
      if (this.pointPolygon) {
        this.pointPolygon.destroy()
        this.pointPolygon = null
      }
      if (this.pointPolygon2) {
        this.pointPolygon2.destroy()
        this.pointPolygon2 = null
      }
      this.pointList.forEach((point) => {
        point.destroy()
      })
      this.pointList = []
      if (this.layer) {
        this.layer.draw()
      }
    },
    // 画多边形
    drawPolygon() {
      this.clearPolygon()
      const me = this
      this.stage.on('mousedown', function (e) {
        const button = e.evt.button
        // 如果是右键，清除多边形
        if (button === 2) {
          me.clearPolygon()
          return
        }
        // 如果点击的是点，可以拖动
        if (e.target && e.target.hasName('circle')) {
          me.dragPoint = e.target

          // 如果点击的第一个点，并且多边形没有闭合，使多边形闭合
          if (
            me.pointPolygon &&
            !me.pointPolygon.getAttr('closed') &&
            me.pointList.length > 2 &&
            e.target === me.pointList[0]
          ) {
            me.pointPolygon.setAttrs({ closed: true })
            me.pointPolygon2.setAttrs({ closed: true })
            me.layer.draw()
          }
          return
        }
        if (me.pointPolygon && me.pointPolygon.getAttr('closed')) {
          return
        }
        if (
          me.pointPolygon &&
          !me.pointPolygon.getAttr('closed') &&
          me.polygonLimit &&
          me.pointList.length >= me.polygonLimit
        ) {
          me.$message({
            type: 'info',
            message: '最多画' + me.polygonLimit + '个边界点'
          })
          return
        }
        // 控制点不能超过图片外
        // const nodes = me.layer.find("Image");
        // const attr = nodes[0].attrs;
        let x = e.evt.offsetX,
          y = e.evt.offsetY
        // if (
        //   x < attr.x - 5 ||
        //   y < attr.y - 5 ||
        //   x > attr.x + attr.width + 5 ||
        //   y > attr.y + attr.height + 5
        // ) {
        //   return;
        // }
        // if (x < attr.x) {
        //   x = attr.x;
        // }
        // if (x > attr.x + attr.width) {
        //   x = attr.x + attr.width;
        // }
        // if (y < attr.y) {
        //   y = attr.y;
        // }
        // if (y > attr.y + attr.height) {
        //   y = attr.y + attr.height;
        // }
        let circle = new Konva.Circle({
          x,
          y,
          radius: 5,
          fill: '#fff',
          name: 'circle',
          stroke: 'black',
          draggable: true
          // dragBoundFunc: function (pos) {
          //   let x = pos.x;
          //   let y = pos.y;
          //   // 控制点不能超过图片外
          //   const nodes = me.layer.find("Image");
          //   const attr = nodes[0].attrs;
          //   if (x < attr.x) {
          //     x = attr.x;
          //   }
          //   if (x > attr.x + attr.width) {
          //     x = attr.x + attr.width;
          //   }
          //   if (y < attr.y) {
          //     y = attr.y;
          //   }
          //   if (y > attr.y + attr.height) {
          //     y = attr.y + attr.height;
          //   }
          //   var scale = me.stage.scaleX();
          //   return {
          //     x: x / scale,
          //     y: y / scale,
          //   };
          // },
        })
        circle.scale({ x: 1 / me.stage.scaleX(), y: 1 / me.stage.scaleX() })
        me.pointList.push(circle)
        me.layer.add(circle)
        circle.zIndex(3)
        circle.absolutePosition({ x: e.evt.offsetX, y: e.evt.offsetY })

        if (!me.pointPolygon && me.pointList.length > 1) {
          var poly = new Konva.Line({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat(),
            stroke: '#000',
            strokeWidth: 1,
            dash: [5],
            closed: false
          })
          var poly2 = new Konva.Line({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat(),
            stroke: '#fff',
            strokeWidth: 2,
            dash: [5],
            closed: false
          })
          me.pointPolygon = poly
          me.pointPolygon2 = poly2
          me.layer.add(poly)
          me.layer.add(poly2)
          poly2.zIndex(1)
          poly.zIndex(2)
        } else if (me.pointPolygon && me.pointList.length > 1) {
          me.pointPolygon.setAttrs({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat(),
            closed: me.polygonLimit && me.pointList.length >= me.polygonLimit
          })
          me.pointPolygon2.setAttrs({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat(),
            closed: me.polygonLimit && me.pointList.length >= me.polygonLimit
          })
        }
        me.layer.draw()
      })

      // 鼠标移动
      this.stage.on('mousemove', function () {
        // console.log(e.evt.offsetX, e.evt.offsetY);
        if (me.dragPoint && me.pointPolygon) {
          // me.pointPolygon.setAttrs({
          //   points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat(),
          // });
          // me.layer.draw();
        }
      })

      // 鼠标放开
      this.stage.on('mouseup', function () {
        if (me.dragPoint && me.pointPolygon) {
          me.pointPolygon.setAttrs({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat()
          })
          me.pointPolygon2.setAttrs({
            points: me.pointList.map((p) => [p.attrs.x, p.attrs.y]).flat()
          })
          me.layer.draw()
        }
        me.dragPoint = null
      })
    },
    clearRect() {
      // 移除图形选择框
      if (this.transformer) {
        this.transformer.destroy()
        this.transformer = null
      }
      if (this.graphNow) {
        this.graphNow.destroy()
        this.graphNow = null
      }
      if (this.layer) {
        this.layer.draw()
      }
    },
    // 添加裁剪框
    addCropLayer() {
      // const node = this.layer.getChildren(
      //   (node) => node.getClassName() === "Image"
      // );
      // const attrs = node[0].attrs;
      // // 裁剪默认框出来
      // if (this.type === "crop") {
      //   this.drawRect(attrs.x, attrs.y, attrs.width, attrs.height, "white", 1);
      // }
      const me = this
      // 移除改变大小事件
      this.stage.on('mousedown', function (e) {
        const button = e.evt.button
        // 如果是右键
        if (button === 2) {
          console.log(button)
          me.stage.draggable(true)
          me.layer.draw()
          return
        }
        // 如果点击空白处 移除图形选择框
        if (e.target) {
          // if (e.target.getType() === 'Stage') {
          //   me.stage.draggable(true)
          //   return
          // } else {
          //   me.stage.draggable(false)
          // }
          if (me.graphNow) {
            return
          }
          me.stage.draggable(false)
          me.drawing = true
          me.stageMousedown(e)

          // 移除图形选择框
          // stage.find("Transformer").destroy();
          me.layer.draw()
          return
        }
        // 如果没有匹配到就终止往下执行
        if (
          !e.target.hasName('line') &&
          !e.target.hasName('ellipse') &&
          !e.target.hasName('rect') &&
          !e.target.hasName('circle')
        ) {
          return
        }
        // 移除图形选择框
        if (me.transformer) {
          me.transformer.destroy()
        }

        // 当前点击的对象赋值给graphNow
        me.graphNow = e.target
        me.layer.draw()
      })

      // 鼠标移动
      this.stage.on('mousemove', function (ev) {
        if (me.graphNow && me.drawing) {
          me.graphNow.setAttrs({
            width: (ev.evt.offsetX - me.pointStart[0]) / me.stage.scaleX(),
            height: (ev.evt.offsetY - me.pointStart[1]) / me.stage.scaleX()
          })
          me.layer.draw()
        }
      })

      // 鼠标放开
      this.stage.on('mouseup', function () {
        me.drawing = false
        me.stage.draggable(true)
        // 如果需要立即执行的回调，并清除rect
        if (me.graphNow && me.operation && !me.operation.confirm) {
          const rect = me.getCropRect()
          if (rect.width < 5 && rect.height < 5) {
          } else {
            me.$emit('crop', rect)
          }
          setTimeout(() => {
            me.clearRect()
          }, 100)
        }
      })
    },

    drawRect(x, y, w, h, c, sw) {
      const me = this
      const rect = new Konva.Rect({
        name: 'rect',
        x: x,
        y: y,
        width: w,
        height: h,
        fill: null,
        stroke: null,
        // fill: sw === 0 ? c : null,
        // stroke: sw > 0 ? c : null,
        strokeWidth: sw,
        opacity: sw === 0 ? 0.5 : 1,
        draggable: true
      })
      me.graphNow = rect
      this.layer.add(rect)
      this.layer.draw()

      rect.on('mouseenter', function () {
        me.stage.container().style.cursor = 'move'
      })

      rect.on('mouseleave', function () {
        me.stage.container().style.cursor = 'default'
      })

      const tr = new Konva.Transformer({
        borderStroke: '#000', // 虚线颜色
        borderStrokeWidth: 1, //虚线大小
        borderDash: [5], // 虚线间距
        keepRatio: false, // 不等比缩放
        rotationSnaps: [0, 90, 180, 270],
        rotationSnapTolerance: 2,
        rotateEnabled: false,
        anchorSize: 8,
        anchorCornerRadius: 8
      })
      this.transformer = tr
      this.layer.add(tr)
      tr.nodes([this.graphNow])
      rect.absolutePosition({ x, y })
      this.layer.draw()
    },

    stageMousedown(ev) {
      let x = ev.evt.offsetX,
        y = ev.evt.offsetY
      this.pointStart = [x, y]

      this.drawRect(x, y, 0, 0, 'red', 1)
      this.drawing = true
    },

    // 获取矩形相对图片大小
    getCropRect() {
      if (this.graphNow && this.imageNow) {
        const crop = this.graphNow.attrs
        const img = this.imageNow.attrs
        const naturalWidth = img.image.naturalWidth
        const scale = naturalWidth / img.width
        // 图片左上角和右下角点坐标
        const imgPoint = {
          x1: img.x,
          y1: img.y,
          x2: img.x + img.width,
          y2: img.y + img.height
        }
        const x1 = crop.x,
          y1 = crop.y,
          x2 = crop.x + crop.width,
          y2 = crop.y + crop.height
        // 矩形左上角和右下角坐标
        const cropPoint = {
          x1: Math.min(x1, x2),
          y1: Math.min(y1, y2),
          x2: Math.max(x1, x2),
          y2: Math.max(y1, y2)
        }
        // 重叠区域左上角和右下角坐标
        const overPoint = {
          x1: Math.max(imgPoint.x1, cropPoint.x1),
          y1: Math.max(imgPoint.y1, cropPoint.y1),
          x2: Math.min(imgPoint.x2, cropPoint.x2),
          y2: Math.min(imgPoint.y2, cropPoint.y2)
        }
        const rs = {
          x: overPoint.x1 - img.x,
          y: overPoint.y1 - img.y,
          width: overPoint.x2 - overPoint.x1,
          height: overPoint.y2 - overPoint.y1
        }
        return {
          x: Number((rs.x * scale).toFixed(2)),
          y: Number((rs.y * scale).toFixed(2)),
          width: Number((rs.width * scale).toFixed(2)),
          height: Number((rs.height * scale).toFixed(2))
        }
      }
    },

    // 获取多边形点相对图片位置
    getPolygonPoints() {
      const img = this.imageNow.attrs
      const naturalWidth = img.image.naturalWidth
      const scale = naturalWidth / img.width
      if (this.polygonLimit && this.pointList.length < this.polygonLimit) {
        return []
      }
      return this.pointList.map((point) => {
        const x = point.attrs.x - img.x
        const y = point.attrs.y - img.y
        return [
          x < 0 ? 0 : Number((x * scale).toFixed(2)),
          y < 0 ? 0 : Number((y * scale).toFixed(2))
        ]
      })
    }
  }
}
</script>

<style scoped>
.rev-image {
  width: 100%;
  height: 100%;
}
</style>
